package org.dailydev.ant.birt.task;

import java.io.File;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.Java;
import org.apache.tools.ant.types.EnumeratedAttribute;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.LogLevel;
import org.apache.tools.ant.types.Parameter;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Commandline.Argument;
import org.apache.tools.ant.types.resources.FileResource;

/**
 * org.eclipse.birt.report.engine.impl.ReportRunner Usage:
 * --mode/-m [ run | render | runrender] the default is runrender
 * for runrender mode:
 *      we should add it in the end <design file>
 *      --format/-f [ HTML \| PDF ]
 *      --output/-o <target file>
 *      --htmlType/-t < HTML \| ReportletNoCSS >
 *      --locale /-l <locale>
 *      --parameter/-p <"parameterName=parameterValue">
 *      --file/-F <parameter file>
 *      --encoding/-e <target encoding>
 *
 * Locale: default is english
 * parameters in command line will overide parameters in parameter file
 * parameter name cant include characters such as \ ', '=', ':'
 *
 * For RUN mode:
 *      we should add it in the end<design file>
 *      --output/-o <target file>
 *      --locale /-l <locale>
 *      --parameter/-p <parameterName=parameterValue>
 *      --file/-F <parameter file>
 *
 * Locale: default is english
 * parameters in command line will overide parameters in parameter file 
 * parameter name cant include characters such as \ ', '=', ':' 
 *
 * For RENDER mode:
 *     we should add it in the end<design file>
 *     --output/-o <target file>
 *     --page/-p <pageNumber>
 *     --locale /-l <locale>
 * 
 * Locale: default is english
 *
 * @author bosakrom
 */
public class BirtTask extends Task {

	private static final String REPORT_RUNNER_CLASS_NAME = "org.eclipse.birt.report.engine.api.ReportRunner";
	private static final String BIRT_HOME_SYSTEM_PROPERTY_NAME = "BIRT_HOME";
	
	private static final String ARGUMENT__MODE = "--mode";
	private static final String ARGUMENT__FORMAT = "--format";
	private static final String ARGUMENT__HTML_TYPE = "--htmlType";
	private static final String ARGUMENT__LOCALE_TYPE = "--locale";
	private static final String ARGUMENT__PARAMETER_FILE = "--file";
	private static final String ARGUMENT__PARAMETER = "--parameter";
	private static final String ARGUMENT__OUTPUT_DIR = "--output";
	private static final String ARGUMENT__ENCODING = "--encoding";
	private static final String ARGUMENT__PAGE = "--page";

	protected String birtHome;

	protected String mode = new ModeEnum().getValues()[0];
	protected String format = new FormatEnum().getValues()[0];
	protected boolean htmlReportlet = false;
	protected String locale = null;
	
	/**
	 * Sets value of 'encoding' attribute of the task.
	 */
	protected String encoding = null;

	protected FileSet reports;
	protected File outputDir;

	protected File parameterFile;
	protected List parameters = new Vector();
	protected int page;

	protected Java javaDelegate;
	
	/** {@inheritDoc} */
	public void execute() throws BuildException {
		checkBirtHomeSpecified();
		
		javaDelegate = new Java();
		javaDelegate.setProject(this.getProject());
		javaDelegate.setFork(true);

		defineClassName();
		defineClasspath();
		defineBirtHomeSystemProperty();

		defineModeArg();
		defineFormatArg();
		defineHtmlTypeArg();
		defineLocaleArg();
		defineEncodingArg();
		definePageArg();
		
		defineParameterFileArg();
		defineParameters();
		
		Argument output = javaDelegate.createArg();
		Argument input = javaDelegate.createArg();
		for (Iterator iterator = reports.iterator(); iterator.hasNext();) {
			FileResource file = (FileResource) iterator.next();

			String outputDirPath = defineOutputArg(output, file);
			String inputFile = defineInput(input, file);
			
			getProject().log(this, "Generating report '" + inputFile + "' to '" + outputDirPath + " ...", LogLevel.INFO.getLevel());
			
			javaDelegate.execute();
			
			getProject().log(this, "Report generation finished.", LogLevel.VERBOSE.getLevel());
		}
	}

	/**
	 * Defines argument '--mode/-m'.
	 */
	protected void defineModeArg() {
		javaDelegate.createArg().setLine(ARGUMENT__MODE + " " + mode);
	}

	/**
	 * Defines argument '--format/-f'. Applicable for 'runrender' and 'render' modes only.
	 */
	protected void defineFormatArg() {
		if(!this.mode.equalsIgnoreCase(ModeEnum.RUN)) {
			javaDelegate.createArg().setLine(ARGUMENT__FORMAT + " " + format);
		}
	}
	
	/**
	 * Defines argument '--output/-o'. Applicable for all modes.
	 * 
	 * @param output argument to be updated
	 * @param file	input file to get relative path from
	 * @return	output dir path
	 */
	protected String defineOutputArg(Argument output, FileResource file) {
		String relativeDir = new File(file.getName()).getParent();
		String outputDirPath = relativeDir;
		
		if (outputDir != null) {
			outputDirPath = outputDir.getAbsolutePath() + "/" + relativeDir;
		}
		
		output.setLine(ARGUMENT__OUTPUT_DIR + " " + outputDirPath);
		return outputDirPath;
	}

	/**
	 * Defines argument '--htmlType/-t'. Applicable for 'html' format and 'runrender' and 'render' modes only.
	 */
	protected void defineHtmlTypeArg() {
		if(!this.mode.equalsIgnoreCase(ModeEnum.RUN) && this.format.equalsIgnoreCase(FormatEnum.HTML)) {
			String htmlType = HtmlTypeEnum.HTML;
			if(htmlReportlet) {
				htmlType = HtmlTypeEnum.REPORTLET;
			}
			javaDelegate.createArg().setLine(ARGUMENT__HTML_TYPE + " " + htmlType);
		}
	}

	/**
	 * Defines argument '--locale/-l'. Applicable for all modes.
	 */
	protected void defineLocaleArg() {
		if(this.locale != null) {
			javaDelegate.createArg().setLine(ARGUMENT__LOCALE_TYPE + " " + this.locale);
		}
	}
	
	/**
	 * Defines arguments '--parameter/-p'. Applicable for 'runrender' and 'run' mode only.
	 */
	protected void defineParameters() {
		if(!this.mode.equalsIgnoreCase(ModeEnum.RENDER)) {
			for (Iterator iterator = parameters.iterator(); iterator.hasNext();) {
				Parameter parameter = (Parameter) iterator.next();
				Argument paramArg = javaDelegate.createArg();
				paramArg.setLine(ARGUMENT__PARAMETER + " \"" + parameter.getName() + "=" + parameter.getValue() + "\"");
			}
		}
	}
	
	/**
	 * Defines '--file/-F' argument. Applicable for 'runrender' and 'run' mode only.
	 */
	protected void defineParameterFileArg() {
		if (!this.mode.equalsIgnoreCase(ModeEnum.RENDER) && parameterFile != null) {
			javaDelegate.createArg().setLine(ARGUMENT__PARAMETER_FILE + " " + parameterFile.getAbsolutePath());
		}
	}
	
	/**
	 * Defines '--encoding/-e' argument. Applicable for 'runrender' mode only.
	 */
	// FIXME >> is it really applicable just 'runrender' mode only?
	protected void defineEncodingArg() {
		if (encoding != null && this.mode.equalsIgnoreCase(ModeEnum.RUNRENDER)) {
			javaDelegate.createArg().setLine(ARGUMENT__ENCODING + " " + encoding);
		}
	}
	
	/**
	 * Defines '--page/-p' argument. Applicable for 'render' mode only.
	 */
	protected void definePageArg() {
		if (page > 0 && this.mode.equalsIgnoreCase(ModeEnum.RENDER)) {
			javaDelegate.createArg().setLine(ARGUMENT__PAGE + " " + page);
		}
	}

	/**
	 * 
	 */
	protected void defineBirtHomeSystemProperty() {
		javaDelegate.createJvmarg().setLine("-DBIRT_HOME=" + getBirtHome() + "/ReportEngine");
	}
	
	/**
	 * 
	 */
	protected void checkBirtHomeSpecified() {
		if(birtHome == null && System.getenv(BIRT_HOME_SYSTEM_PROPERTY_NAME) == null) {
			throw new BuildException("BIRT_HOME system property or birtHome element should be defined.");
		}
	}
	
	/**
	 * @return
	 */
	protected String getBirtHome() {
		checkBirtHomeSpecified();
		if(birtHome != null) {
			return birtHome;
		} else {
			return System.getenv(BIRT_HOME_SYSTEM_PROPERTY_NAME);
		}
	}

	/**
	 * 
	 */
	protected void defineClasspath() {
		FileSet classPathFileSet = new FileSet();
		classPathFileSet.setDir(new File(getBirtHome() + "/ReportEngine/lib"));
		classPathFileSet.setIncludes("*.jar");

		Path classPath = new Path(this.getProject());
		classPath.addFileset(classPathFileSet);

		javaDelegate.setClasspath(classPath);
	}

	/**
	 * 
	 */
	protected void defineClassName() {
		javaDelegate.setClassname(REPORT_RUNNER_CLASS_NAME);
	}

	/**
	 * @param paramFile
	 */
	public void setParameterFile(File paramFile) {
		getProject().log(this, "parameterFile: " + paramFile.getAbsolutePath(), LogLevel.DEBUG.getLevel());
		this.parameterFile = paramFile;
	}

	/**
	 * @param birtHomeParam
	 */
	public void setBirthome(String birtHomeParam) {
		getProject().log(this, "birthome: " + birtHomeParam, LogLevel.DEBUG.getLevel());
		if (birtHomeParam != null) {
			this.birtHome = birtHomeParam;
		}
	}

	/**
	 * @param mode
	 */
	public void setMode(ModeEnum mode) {
		getProject().log(this, "mode: " + mode.getValue(), LogLevel.DEBUG.getLevel());
		if (mode.getValue() != null) {
			this.mode = mode.getValue();
		}
	}

	/**
	 * @param format
	 */
	public void setFormat(FormatEnum format) {
		getProject().log(this, "format: " + format.getValue(), LogLevel.DEBUG.getLevel());
		if (format.getValue() != null) {
			this.format = format.getValue();
		}
	}

	/**
	 * @param htmlType
	 */
	public void setHtmlReportlet(boolean htmlReportlet) {
		getProject().log(this, "htmlReportlet: " + htmlReportlet, LogLevel.DEBUG.getLevel());
		this.htmlReportlet = htmlReportlet;
	}

	/**
	 * @param dir
	 */
	public void setOutputdir(File dir) {
		getProject().log(this, "outputdir: " + dir.getAbsolutePath(), LogLevel.DEBUG.getLevel());
		this.outputDir = dir;
	}
	
	/**
	 * @param locale
	 */
	public void setLocale(String locale) {
		getProject().log(this, "locale: " + locale, LogLevel.DEBUG.getLevel());
		this.locale = locale;
	}

	/**
	 * Sets value of 'encoding' attribute of the task.
	 * 
	 * @param encoding
	 */
	public void setEncoding(String encoding) {
		this.encoding = encoding;
	}

	/**
	 * @param page
	 */
	public void setPage(int page) {
		this.page = page;
	}

	/**
	 * @return
	 */
	public FileSet createFileset() {
		if (this.reports == null) {
			this.reports = new FileSet();
		}
		return this.reports;
	}
	
	/**
	 * @return
	 */
	public Parameter createParam() {
		Parameter parameter = new Parameter();
		parameters.add(parameter);
		return parameter;
	}
	


	protected String defineInput(Argument input, FileResource file) {
		String inputFile = file.getFile().getAbsolutePath();
		input.setLine(inputFile);
		return inputFile;
	}

	/**
	 * TODO Add description of this class
	 * 
	 * @author bosakrom
	 */
	public static class ModeEnum extends EnumeratedAttribute {

		public static final String RUNRENDER = "runrender";
		public static final String RUN = "run";
		public static final String RENDER = "render";

		/**
		 * TODO Add description of this constructor
		 * 
		 */
		public ModeEnum() {
		}

		/** {@inheritDoc} */
		public String[] getValues() {
			return new String[] { RUNRENDER, RUN, RENDER };
		}
	}

	/**
	 * TODO Add description of this class
	 * 
	 * @author bosakrom
	 */
	public static class FormatEnum extends EnumeratedAttribute {

		public static final String HTML = "html";
		public static final String PDF = "pdf";

		/**
		 * TODO Add description of this constructor
		 * 
		 */
		public FormatEnum() {
		}

		/** {@inheritDoc} */
		public String[] getValues() {
			return new String[] { HTML, PDF };
		}
	}

	/**
	 * TODO Add description of this class
	 * 
	 * @author bosakrom
	 */
	public static class HtmlTypeEnum extends EnumeratedAttribute {
		
		private static final String HTML = "HTML";
		private static final String REPORTLET = "ReportletNoCSS";
		
		/**
		 * TODO Add description of this constructor
		 * 
		 */
		public HtmlTypeEnum() {
		}

		/** {@inheritDoc} */
		public String[] getValues() {
			return new String[] { HTML, REPORTLET };
		}
	}
}
